/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.tasks;

import java.util.Iterator;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;


import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

import taskgraph.Config;

/**
 * Superclass of all tasks.
 * 
 * @author Armando Blancas
 */
public abstract class Task implements Runnable, Iterable<Task> {
	
	public enum Wait { WAIT, NO_WAIT };

	protected static Logger log;

	static {
		Config cfg = Config.get();
		PropertyConfigurator.configure(cfg.loggerConfig());
		log = Logger.getLogger(cfg.loggerName());
	}
	
	private long start;
	private long finish;
	private Future<?> future;
	
	/**
	 * Gets the task's elapsed time.
	 * 
	 * @return The elapsed time in nanoseconds.
	 */
	public long elapsedTime() {
		assert finish >= start;
		return finish - start;
	}
	
	/**
	 * Executes this task.
	 * 
	 * Executes the receiving task either waiting for it to complete or 
	 * returning immediately depending on the value of {@code wait} being
	 * {@code WAIT} or {@code NO_WAIT} respectively.
	 * 
	 * @param wait The {@code Wait} enum value.
	 * @return A {@code Future} for the running task.
	 * @throws InterruptedException If the task is interrupted while this
	 *         call is waiting for it to end. Client code should handle it.
	 */
	public Future<?> execute(final Wait wait) throws InterruptedException {
		ExecutorService exec = new TaskExecutor();
		final CountDownLatch finish = new CountDownLatch(1);
		Future<?> future = exec.submit(new Runnable() {
			public void run() {
				try {
					markStartTime();
					Task.this.run();
					markFinishTime();
				} finally {
					finish.countDown();
				}
			}
		});
		if (wait == Wait.WAIT) {
			finish.await();
		}
		return future;
	}
	
	/**
	 * Gets a future object to keep track of this task's life-cycle.
	 * This value is set by the task scheduler.
	 * 
	 * @return Fhe {@code Future} object for this task.
	 */
	public Future<?> getFuture() {
		return future;
	}
	
	/**
	 * Gets an iterator for visiting subordinate tasks.
	 * 
	 * @return An {@code Iterator} for access to {@code Task} objects.
	 */
	public abstract Iterator<Task> iterator();


	
	/**
	 * Marks the relative time at which this task ended.
	 */
	protected void markFinishTime() {
		finish = System.nanoTime();
	}

	/**
	 * Marks the relative time at which this task started.
	 */
	protected void markStartTime() {
		start = System.nanoTime();
	}

	/**
	 * Sets the task's future object to keep track of its life-cycle.
	 * This value is set by the task scheduler.
	 * 
	 * @param future The {@code Future} object to set.
	 */
	public void setFuture(final Future<?> future) {
		this.future = future;
	}
	
	/**
	 * Prepares this task for execution.
	 * 
	 * <p>This is an important method for Composite tasks because it is here
	 * where they create and connect their subordinate tasks. For Primitive
	 * tasks this method is optional and usually will be empty.
	 */
    protected abstract void setup();
}
